package com.example.fastexcel.controller;

import ch.qos.logback.core.util.FileUtil;
import com.example.fastexcel.model.ApiResponse;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.xnio.Result;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件上传下载控制器
 * 提供基础的文件上传、下载、删除功能
 */
@Slf4j
@RestController
@RequestMapping("/api/file")
public class FileController {
    
    /**
     * 上传文件存储目录路径
     * 从配置文件中读取
     */
    @Value("${spring.file.upload.dir}")
    private String uploadDir;
    
    /**
     * 允许的文件扩展名列表
     * 从配置文件中读取并转换为List
     */
    @Value("${spring.file.upload.allowed-extensions}")
    private String allowedExtensions;
    
    /**
     * 最大文件大小（字节）
     * 从配置文件中读取
     */
    @Value("${spring.file.upload.max-size}")
    private long maxSize;
    
    /**
     * 初始化上传目录
     */
    @PostConstruct
    public void init() {
        // 创建上传目录（如果不存在）
        File uploadDirectory = new File(uploadDir);
        if (!uploadDirectory.exists()) {
            boolean created = uploadDirectory.mkdirs();
            if (created) {
                log.info("创建上传目录: {}", uploadDir);
            }
        }
        log.info("文件上传目录: {}", uploadDir);
    }
    
    /**
     * 单文件上传接口 https://docs.springframework.org.cn/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/multipart-forms.html
     * 
     * @param file 上传的文件
     * @return 上传结果，包含文件访问路径
     */
    @PostMapping("/upload")
    public ApiResponse<String> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            // 验证文件是否为空
            if (file.isEmpty()) {
                return Result.error("上传文件不能为空");
            }
            
            // 验证文件大小
            if (file.getSize() > maxSize) {
                return Result.error("文件大小不能超过 " + (maxSize / 1024 / 1024) + "MB");
            }
            
            // 验证文件扩展名
            String originalFilename = file.getOriginalFilename();
            if (!isValidFileType(originalFilename)) {
                return Result.error("不支持的文件类型，请上传以下类型文件: " + allowedExtensions);
            }
            
            // 生成唯一的文件名（避免文件名冲突）
            String uniqueFilename = generateUniqueFilename(originalFilename);
            
            // 构建完整的文件路径
            Path filePath = Paths.get(uploadDir, uniqueFilename);
            
            // 保存文件到磁盘
            file.transferTo(filePath.toFile());
            
            // 返回文件访问路径（相对路径）
            String fileUrl = "/api/file/download/" + uniqueFilename;
            
            log.info("文件上传成功: 原始文件名={}, 保存路径={}", originalFilename, filePath);
            return Result.success("文件上传成功", fileUrl);
            
        } catch (IOException e) {
            log.error("文件上传失败", e);
            return Result.error("文件上传失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("文件上传异常", e);
            return Result.error("文件上传异常: " + e.getMessage());
        }
    }
    
    /**
     * 多文件上传接口
     * 
     * @param files 上传的文件数组
     * @return 上传结果列表，包含每个文件的访问路径
     */
    @PostMapping("/upload/batch")
    public ApiResponse<List<String>> uploadFiles(@RequestParam("files") MultipartFile[] files) {
        if (files == null || files.length == 0) {
            return Result.error("上传文件不能为空");
        }
        
        // 限制一次上传的文件数量（可选）
        if (files.length > 10) {
            return Result.error("一次最多上传10个文件");
        }
        
        try {
            List<String> fileUrls = Arrays.stream(files)
                .filter(file -> !file.isEmpty()) // 过滤空文件
                .map(this::processSingleFile)    // 处理单个文件
                .filter(result -> result.getCode() == 200) // 过滤成功的结果
                .map(Result::getData)            // 提取文件URL
                .collect(Collectors.toList());
            
            if (fileUrls.isEmpty()) {
                return Result.error("没有有效的文件被上传");
            }
            
            log.info("批量文件上传成功，共上传 {} 个文件", fileUrls.size());
            return Result.success("批量文件上传成功", fileUrls);
            
        } catch (Exception e) {
            log.error("批量文件上传失败", e);
            return Result.error("批量文件上传失败: " + e.getMessage());
        }
    }
    
    /**
     * 处理单个文件上传的私有方法
     * 
     * @param file 单个文件
     * @return 上传结果
     */
    private ApiResponse<String> processSingleFile(MultipartFile file) {
        try {
            // 验证文件大小
            if (file.getSize() > maxSize) {
                return Result.error("文件 " + file.getOriginalFilename() + " 大小超过限制");
            }
            
            // 验证文件扩展名
            String originalFilename = file.getOriginalFilename();
            if (!isValidFileType(originalFilename)) {
                return Result.error("文件 " + originalFilename + " 类型不支持");
            }
            
            // 生成唯一文件名
            String uniqueFilename = generateUniqueFilename(originalFilename);
            Path filePath = Paths.get(uploadDir, uniqueFilename);
            
            // 保存文件
            file.transferTo(filePath.toFile());
            
            return Result.success("/api/file/download/" + uniqueFilename);
            
        } catch (IOException e) {
            log.error("处理文件 {} 失败", file.getOriginalFilename(), e);
            return Result.error("处理文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 文件下载接口
     * 
     * @param filename 要下载的文件名
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    @GetMapping("/download/{filename:.+}")
    public void downloadFile(@PathVariable String filename, HttpServletResponse response) throws IOException {
        // 验证文件名安全性（防止路径遍历攻击）
        if (!isValidFilename(filename)) {
            response.sendError(HttpStatus.BAD_REQUEST.value(), "无效的文件名");
            return;
        }
        
        // 构建文件完整路径
        Path filePath = Paths.get(uploadDir, filename);
        
        // 检查文件是否存在
        if (!Files.exists(filePath)) {
            response.sendError(HttpStatus.NOT_FOUND.value(), "文件不存在");
            return;
        }
        
        // 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setContentLengthLong(Files.size(filePath));
        
        // 设置文件名（处理中文文件名）
        String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8)
            .replaceAll("\\+", "%20"); // 处理空格
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, 
            "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename);
        
        // 写入文件内容到响应
        Files.copy(filePath, response.getOutputStream());
        response.getOutputStream().flush();
    }
    
    /**
     * 通过文件名直接下载（返回ResponseEntity方式）
     * 这种方式更适合需要返回JSON错误信息的场景
     * 
     * @param filename 要下载的文件名
     * @return 文件流响应
     */
    @GetMapping("/download2/{filename:.+}")
    public ApiResponse<byte[]> downloadFile2(@PathVariable String filename) {
        try {
            // 验证文件名安全性
            if (!isValidFilename(filename)) {
                return ResponseEntity.badRequest().build();
            }
            
            // 构建文件路径
            Path filePath = Paths.get(uploadDir, filename);
            
            // 检查文件是否存在
            if (!Files.exists(filePath)) {
                return ResponseEntity.notFound().build();
            }
            
            // 读取文件内容
            byte[] fileContent = Files.readAllBytes(filePath);
            
            // 构建响应头
            String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20");
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentLength(fileContent.length);
            headers.setContentDispositionFormData("attachment", encodedFilename);
            
            return ResponseEntity.ok()
                .headers(headers)
                .body(fileContent);
                
        } catch (IOException e) {
            log.error("文件下载失败: {}", filename, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * 删除文件接口
     * 
     * @param filename 要删除的文件名
     * @return 删除结果
     */
    @DeleteMapping("/delete/{filename:.+}")
    public ApiResponse<String> deleteFile(@PathVariable String filename) {
        try {
            // 验证文件名安全性
            if (!isValidFilename(filename)) {
                return Result.error("无效的文件名");
            }
            
            // 构建文件路径
            Path filePath = Paths.get(uploadDir, filename);
            
            // 检查文件是否存在
            if (!Files.exists(filePath)) {
                return Result.error("文件不存在");
            }
            
            // 删除文件
            boolean deleted = Files.deleteIfExists(filePath);
            if (deleted) {
                log.info("文件删除成功: {}", filename);
                return Result.success("文件删除成功");
            } else {
                return Result.error("文件删除失败");
            }
            
        } catch (IOException e) {
            log.error("文件删除失败: {}", filename, e);
            return Result.error("文件删除失败: " + e.getMessage());
        }
    }
    
    /**
     * 验证文件扩展名是否合法
     * 
     * @param filename 文件名
     * @return 是否合法
     */
    private boolean isValidFileType(String filename) {
        if (!StringUtils.hasText(filename)) {
            return false;
        }
        
        // 获取文件扩展名（转换为小写）
        String extension = FileUtil.extName(filename).toLowerCase();
        
        // 检查扩展名是否在允许列表中
        List<String> allowedList = Arrays.asList(allowedExtensions.split(","));
        return allowedList.contains(extension);
    }
    
    /**
     * 验证文件名安全性（防止路径遍历攻击）
     * 
     * @param filename 文件名
     * @return 是否安全
     */
    private boolean isValidFilename(String filename) {
        if (!StringUtils.hasText(filename)) {
            return false;
        }
        
        // 检查是否包含路径分隔符（防止路径遍历）
        if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
            return false;
        }
        
        // 检查文件名长度
        if (filename.length() > 255) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 生成唯一的文件名
     * 使用UUID + 原始文件扩展名
     * 
     * @param originalFilename 原始文件名
     * @return 唯一文件名
     */
    private String generateUniqueFilename(String originalFilename) {
        // 获取文件扩展名
        String extension = FileUtil.extName(originalFilename);
        
        // 生成UUID（使用Hutool的IdUtil）
        String uuid = IdUtil.fastSimpleUUID();
        
        // 如果原始文件没有扩展名，则不添加扩展名
        if (StrUtil.isBlank(extension)) {
            return uuid;
        } else {
            return uuid + "." + extension;
        }
    }
}
